home *** CD-ROM | disk | FTP | other *** search
- PROGRAM TicTacTo;
- Uses WinTypes, WinProcs, WObjects;
- {$R TicTacTo}
- {$D Copyright (c) 1991 by Neil J. Rubenking}
- CONST
- AppName : PChar = 'TicTacTo';
- cm_CCs = 102;
- cm_CPs = 103;
- cm_PXs = 104;
- cm_POs = 105;
- cm_Help = 106;
- Xv = 1; X2 = 2*Xv;
- Ov = 4; O2 = 2*Ov;
- Draw = 255;
-
- TYPE
- TMyApplication = object(TApplication)
- PROCEDURE InitMainWindow; virtual;
- END;
-
- PTicWindow = ^TTicWindow;
- TTicWindow = OBJECT(TWindow)
- Rects : ARRAY[0..8] OF TRect;
- Plays : ARRAY[0..9] OF Byte;
- IsX, UseComp : Boolean;
- Moves, PenWid : Word;
- CONSTRUCTOR Init(AParent : PWindowsObject; AName : PChar);
- FUNCTION GetClassName : PChar; Virtual;
- PROCEDURE GetWindowClass(var AWndClass: TWndClass); Virtual;
- PROCEDURE Paint(pDC : hDC; VAR PS : TPaintStruct); Virtual;
- PROCEDURE NewGame(XStart, vsComp : Boolean);
- PROCEDURE PlayAndCheck(NewSpot : Word);
- PROCEDURE ProgPlay;
- PROCEDURE wmLButtonDown(VAR Msg : TMessage);
- Virtual wm_First + wm_LButtonDown;
- PROCEDURE WMKeyDown(VAR Msg : TMessage);
- Virtual wm_First + wm_KeyDown;
- PROCEDURE wmNCHitTest(VAR Msg : TMessage);
- Virtual wm_First + wm_NCHitTest;
- PROCEDURE DefCommandProc(VAR Msg : TMessage); Virtual;
- END;
-
- {--------------------------------------------------}
- { TTicWindow's methods }
- {--------------------------------------------------}
- CONSTRUCTOR TTicWindow.Init(AParent : PWindowsObject; AName : PChar);
- BEGIN
- TWindow.Init(AParent, AName);
- Attr.Menu := LoadMenu(hInstance, AppName);
- NewGame(TRUE, TRUE);
- Randomize;
- END;
-
- FUNCTION TTicWindow.GetClassName;
- BEGIN GetClassName := AppName; END;
-
- PROCEDURE TTicWindow.GetWindowClass(VAR AWndClass : TWndClass);
- BEGIN
- TWindow.GetWindowClass(AWndClass);
- AWndClass.hIcon := LoadIcon(HInstance, AppName);
- AWndClass.hCursor := 0;
- END;
-
- {x$DEFINE ShowSlow}
- PROCEDURE TTicWindow.Paint(pDC : hDC; VAR PS : TPaintStruct);
- VAR X, Y, X3, Y3, X16, Y16, N : Integer;
- hp, oldp : hPen;
- TR : TRect;
- CONST Blue = $00FF0000;
- Red = $000000FF;
-
- PROCEDURE OneSquare(N : Integer);
- {$IFDEF ShowSlow}
- VAR Slow : LongInt;
- {100000 is for 486/33 - reduce for slower machines}
- CONST Factor = 100000;
- {$ENDIF}
- BEGIN
- {$IFDEF ShowSlow}
- FOR Slow := 1 to Factor DO N := N;
- {$ENDIF}
- CASE Plays[N] OF
- Xv: BEGIN
- hp := CreatePen(ps_Solid, PenWid, Red);
- oldp := SelectObject(pDC, hp);
- WITH Rects[N] DO
- BEGIN
- MoveTo(pDC, Left, Top); LineTo(pDC, Right, Bottom);
- MoveTo(pDC, Right, Top); LineTo(pDC, Left, Bottom);
- END;
- SelectObject(pDC, OldP);
- DeleteObject(hP);
- END;
- Ov: BEGIN
- hp := CreatePen(ps_Solid, PenWid, blue);
- oldp := SelectObject(pDC, hp);
- WITH Rects[N] DO Ellipse(pDC, Left, Top, RIght, Bottom);
- SelectObject(pDC, OldP);
- DeleteObject(hP);
- END;
- END;
- END;
-
- BEGIN
- GetClientRect(hWindow, TR);
- X := TR.Right; Y := TR.Bottom;
- X3 := X DIV 3; Y3 := Y DIV 3;
- X16 := X DIV 16; Y16 := Y DIV 16;
- IF X16 < Y16 THEN PenWid := 2*X16 DIV 3
- ELSE PenWid := 2*Y16 DIV 3;
- IF EqualRect(TR, PS.rcPaint) THEN {paint whole window}
- BEGIN
- {draw the # diagram}
- hp := CreatePen(ps_Solid, PenWid, 0);
- oldp := SelectObject(pDC, hp);
- MoveTo(pDC, X3, Y16); LineTo(pDC, X3, Y-Y16);
- MoveTo(pDC, 2*X3, Y16); LineTo(pDC, 2*X3, Y-Y16);
- MoveTo(pDC, X16, Y3); LineTo(pDC, X-X16, Y3);
- MoveTo(pDC, X16, 2*Y3); LineTo(pDC, X-X16, 2*Y3);
- SelectObject(pDC, OldP);
- DeleteObject(hP);
- {establish the "control" rectangles}
- FOR N := 0 to 8 DO
- BEGIN
- SetRect(Rects[N], (N MOD 3)*X3, (N DIV 3)*Y3,
- Succ(N MOD 3)*X3, Succ(N DIV 3)*Y3);
- InflateRect(Rects[N], -X16, -Y16);
- END;
- {draw the X's and O's}
- FOR N := 0 to 8 DO OneSquare(N);
- END
- ELSE {just paint the necessary areas}
- BEGIN
- {paint squares that need it}
- FOR N := 0 to 8 DO
- IF IntersectRect(TR,Rects[N],PS.rcPaint)<>0 THEN
- OneSquare(N);
- {paint lines of the # diagram that need it}
- hp := CreatePen(ps_Solid, PenWid, 0);
- oldp := SelectObject(pDC, hp);
- SetRect(TR, X3-X16, Y16, X3+X16, Y-Y16);
- IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
- BEGIN
- MoveTo(pDC, X3, Y16);
- LineTo(pDC, X3, Y-Y16);
- END;
- SetRect(TR, 2*X3-X16, Y16, 2*X3+X16, Y-Y16);
- IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
- BEGIN
- MoveTo(pDC, 2*X3, Y16);
- LineTo(pDC, 2*X3, Y-Y16);
- END;
- SetRect(TR, X16, Y3-Y16, X-X16, Y3+Y16);
- IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
- BEGIN
- MoveTo(pDC, X16, Y3);
- LineTo(pDC, X-X16, Y3);
- END;
- SetRect(TR, X16, 2*Y3-Y16, X-X16, 2*Y3+Y16);
- IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
- BEGIN
- MoveTo(pDC, X16, 2*Y3);
- LineTo(pDC, X-X16, 2*Y3);
- END;
- SelectObject(pDC, OldP);
- DeleteObject(hP);
- END;
- END;
-
- PROCEDURE TTicWindow.NewGame(XStart, vsComp : Boolean);
- BEGIN
- IsX := XStart; UseComp := vsComp;
- FillChar(Plays, SizeOf(Plays), 0);
- Plays[9] := 127; Moves := 0;
- InvalidateRect(hWindow, NIL, TRUE);
- IF UseCOMP AND (NOT IsX) THEN ProgPlay;
- END;
-
- PROCEDURE TTicWindow.PlayAndCheck(NewSpot : Word);
- VAR TR : TRect;
-
- FUNCTION Won : Byte;
- VAR N : Word;
- BEGIN
- FOR N := 0 to 2 DO
- CASE Plays[N*3+0] + Plays[N*3+1] + Plays[N*3+2] OF
- 3*Xv: BEGIN Won := Xv; Exit; END;
- 3*Ov: BEGIN Won := Ov; Exit; END;
- END;
- FOR N := 0 to 2 DO
- CASE Plays[N+0] + Plays[N+3] + Plays[N+6] OF
- 3*Xv: BEGIN Won := Xv; Exit; END;
- 3*Ov: BEGIN Won := Ov; Exit; END;
- END;
- CASE Plays[0] + Plays[4] + Plays[8] OF
- 3*Xv: BEGIN Won := Xv; Exit; END;
- 3*Ov: BEGIN Won := Ov; Exit; END;
- END;
- CASE Plays[2] + Plays[4] + Plays[6] OF
- 3*Xv: BEGIN Won := Xv; Exit; END;
- 3*Ov: BEGIN Won := Ov; Exit; END;
- END;
- IF Moves = 9 THEN
- BEGIN Won := Draw; Exit; END;
- Won := 0;
- END;
-
- BEGIN
- IF Plays[NewSpot] <> 0 THEN
- BEGIN MessageBeep(0); Exit; END;
- IF IsX THEN Plays[NewSpot] := Xv ELSE Plays[NewSpot] := Ov;
- Inc(Moves);
- IsX := NOT IsX;
- TR := Rects[NewSpot];
- InflateRect(TR, PenWid, PenWid);
- InvalidateRect(hWindow, @Tr, FALSE);
- CASE Won OF
- Xv : BEGIN
- MessageBox(hWindow,'X wins!','A WINNER!', mb_Ok);
- NewGame(IsX XOR Odd(Moves), UseComp);
- END;
- Ov : BEGIN
- MessageBox(hWindow,'O wins!','A WINNER!', mb_Ok);
- NewGame(IsX XOR Odd(Moves), UseComp);
- END;
- Draw : BEGIN
- MessageBox(hWindow,'A Draw!','NO WINNER!', mb_Ok);
- NewGame(NOT IsX, UseComp);
- END;
- ELSE IF UseCOMP AND (NOT IsX) THEN ProgPlay;
- END;
- END;
-
- PROCEDURE TTicWIndow.ProgPlay;
- VAR spot : Word;
- TR : TRect;
- CONST Corners : ARRAY[0..3] OF Byte = (0, 2, 6, 8);
-
- FUNCTION RateThem : Word;
- {NEVER called 'til after middle square (#4) is used}
- VAR N, Best, BestRate, a1, a2, d1, d2,
- g1, g2, ac, dn, dg : Word;
- Ratings : ARRAY[0..8] OF Byte;
-
- PROCEDURE UpdateBest(Num, Value : Word);
- BEGIN
- Ratings[Num] := Value;
- IF Value > BestRate THEN
- BEGIN BestRate := Value; Best := Num; END;
- END;
-
- BEGIN
- Best := 0; BestRate := 0;
- FOR N := 0 to 8 DO
- BEGIN
- IF Plays[N] <> 0 THEN Ratings[N] := 0
- ELSE
- BEGIN
- a1 := (N DIV 3) * 3; a2 := succ(a1);
- IF a1 = N THEN Inc(a1, 2);
- IF a2 = N THEN Inc(a2);
- d1 := N MOD 3; d2 := d1 + 3;
- IF d1 = N THEN Inc(D1, 6);
- IF d2 = N THEN Inc(D2, 3);
- g1 := 4;
- IF Odd(N) THEN
- BEGIN g1 := 9; g2 := 9; END
- ELSE
- CASE N OF
- 0 : g2 := 8;
- 2 : g2 := 6;
- 6 : g2 := 2;
- 8 : g2 := 0;
- END;
- ac := Plays[a1] + Plays[a2];
- dn := Plays[d1] + Plays[d2];
- dg := Plays[g1] + Plays[g2];
- IF (ac=O2) OR (dn=O2) OR (dg=O2) THEN
- UpdateBest(N, 5)
- ELSE IF (ac=X2) OR (dn=X2) OR (dg=X2) THEN
- UpdateBest(N, 4)
- ELSE IF (ac+dn=O2) OR (ac+dg=O2) OR (dn+dg=O2) THEN
- UpdateBest(N, 3)
- ELSE IF (ac=Ov) OR (dn=Ov) OR (dg=Ov) THEN
- UpdateBest(N, 2)
- ELSE UpdateBest(N, 1);
- END;
- END;
- RateThem := Best;
- END;
-
- BEGIN
- CASE Moves OF
- 0 : Spot := 4;
- 1 : BEGIN
- IF Plays[4] = 0 THEN Spot := 4
- ELSE Spot := Corners[Random(4)];
- END;
- ELSE Spot := RateThem;
- END;
- PlayAndCheck(Spot);
- END;
-
- PROCEDURE TTicWindow.WmLButtonDown(VAR Msg : TMessage);
- VAR N : Word;
- BEGIN
- N := 0;
- {determine if the mouse is in any of our rectangles}
- WHILE (N < 9) AND (NOT PtInRect(Rects[N], TPoint(Msg.LParam))) DO
- Inc(N);
- IF N < 9 THEN PlayAndCheck(N);
- END;
-
- PROCEDURE TTicWindow.WMKeyDown(VAR Msg : TMessage);
- VAR T : TPoint;
- N : Integer;
- BEGIN
- GetCursorPos(T);
- ScreenToClient(hWindow, T);
- N := 0;
- {determine if the mouse is in any of our rectangles}
- WHILE (N < 9) AND (NOT PtInRect(Rects[N], T)) DO Inc(N);
- IF N = 9 THEN N := 0
- ELSE
- CASE Msg.wParam OF
- vk_Tab : N := Succ(N) MOD 9;
- vk_Right : IF (N MOD 3) = 2 THEN Dec(N,2) ELSE Inc(N);
- vk_Left : IF (N MOD 3) = 0 THEN Inc(N,2) ELSE Dec(N);
- vk_Down : IF (N DIV 3) = 2 THEN Dec(N, 6) ELSE Inc(N, 3);
- vk_Up : IF (N DIV 3) = 0 THEN Inc(N, 6) ELSE Dec(N, 3);
- vk_Space,
- vk_Return: PlayAndCheck(N);
- END;
- WITH Rects[N] DO
- BEGIN
- T.X := (Right + Left) DIV 2;
- T.Y := (Bottom + Top) DIV 2;
- END;
- ClientToScreen(hWindow, T);
- SetCursorPos(T.X, T.Y);
- DefWndProc(Msg);
- END;
-
- PROCEDURE TTicWindow.WmNCHitTest(VAR Msg : TMessage);
- VAR N : Word;
- Pt : TPoint;
- CurA : hCursor;
- BEGIN
- Move(Msg.Lparam, Pt, 4);
- ScreenToClient(hWindow, Pt);
- N := 0;
- WHILE (N < 9) AND (NOT PtInRect(Rects[N], Pt)) DO
- Inc(N);
- IF N < 9 THEN
- BEGIN
- IF Plays[N] <> 0 THEN CurA := LoadCursor(hInstance, 'CurNO')
- ELSE IF IsX THEN CurA := LoadCursor(hInstance, 'CurX')
- ELSE CurA := LoadCursor(hInstance, 'CurO');
- END
- ELSE CurA := LoadCursor(0, idc_Arrow);
- SetCursor(CurA);
- DefWndProc(Msg);
- END;
-
- PROCEDURE TTicWindow.DefCommandProc(VAR Msg : TMessage);
- VAR PD : PDialog;
- BEGIN
- IF Msg.WParamHi = 0 THEN
- CASE Msg.WParamLo OF
- cm_CCs : NewGame(FALSE, TRUE);
- cm_CPs : NewGame(TRUE, TRUE);
- cm_PXs : NewGame(TRUE, FALSE);
- cm_POs : NewGame(FALSE, FALSE);
- cm_Help : BEGIN
- New(PD, Init(@Self, 'TicHelp'));
- Application^.ExecDialog(PD);
- END;
- ELSE TWindow.DefCommandProc(Msg);
- END;
- END;
-
- {--------------------------------------------------}
- { TMyApplication's method implementations: }
- {--------------------------------------------------}
- PROCEDURE TMyApplication.InitMainWindow;
- BEGIN MainWindow := New(PTicWindow, Init(Nil, AppName)); END;
-
- {--------------------------------------------------}
- { Main program: }
- {--------------------------------------------------}
- VAR MyApp: TMyApplication;
- BEGIN
- MyApp.Init(AppName);
- MyApp.Run;
- MyApp.Done;
- END.